﻿using HDFSViewer.Model;
using Microsoft.Hadoop.WebHDFS;
using Microsoft.Hadoop.WebHDFS.Adapters;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;

namespace HDFSViewer.Component
{
    public class HDFS
    {
        private WebHDFSClient _hdfs = null;
        private static HDFS _instance = null;

        private HDFS() { }
        public static HDFS GetInstance()
        {
            if (HDFS._instance == null) HDFS._instance = new HDFS();
            return HDFS._instance;
        }

        public bool Initiated
        {
            get { return _hdfs != null; }
        }

        public void InitiateHDFS(string uri,string userName)
        {
            _hdfs = new WebHDFSClient(new Uri(uri), userName);
        }

        public void InitiateHDFS(string userName, string account, string key, string containerName, bool autoCreateContainer)
        {
            _hdfs = new WebHDFSClient(userName, new BlobStorageAdapter(account, key, containerName, autoCreateContainer));
        }

        public void Clear()
        {
            _hdfs = null;
        }

        public Task GetHDFSDirectoryStatus(string remotePath, Action<Task<DirectoryListing>> continueTask)
        {
            if (_hdfs != null)
            {
                return _hdfs.GetDirectoryStatus(remotePath).ContinueWith(continueTask);
            }
            else return null;
        }

        public Task CreateHDFSDirectory(string remotePath, Action<Task<bool>> continueTask)
        {
            if (_hdfs != null)
            {
                return _hdfs.CreateDirectory(remotePath).ContinueWith(continueTask);
            }
            else return null;
        }

        public Task DeleteHDFSDirectory(string remotePath, Action<Task<bool>> continueTask)
        {
            if (_hdfs != null)
            {
                return _hdfs.DeleteDirectory(remotePath,true).ContinueWith(continueTask);
            }
            else return null;
        }

        public Task UploadHDFSFile(string remotePath, string localPath, Action<Task<string>> continueTask)
        {
            if (_hdfs != null)
            {
                return _hdfs.CreateFile(localPath, remotePath).ContinueWith(continueTask);
            }
            else return null;
        }

        public Task AddHDFSFile(string remotePath,Stream data,Action<Task<string>> continueTask)
        {
            if (_hdfs != null)
            {
                return _hdfs.CreateFile(data, remotePath).ContinueWith(continueTask);
            }
            else return null;
        }

        //public Task GetHDFSFileChecksum(string remotePath,Action<Task<FileChecksum>> continueTask)
        //{
        //    if (_hdfs != null)
        //    {
        //        return _hdfs.GetFileChecksum(remotePath).ContinueWith(continueTask);
        //    }
        //    else return null;
        //}

        public Task OpenHDFSFile(string remotePath, int offset, int length, Action<Task<HttpResponseMessage>> continueTask)
        {
            if (_hdfs != null)
            {
                return _hdfs.OpenFile(remotePath,offset,length).ContinueWith(continueTask);
            }
            else return null;
        }

        public void DownloadHDFSFile(string remotePath, Stream fileStream, int offset, int length, int total, OperationLogModel log)
        {
            if (_hdfs != null)
            {
                bool islast = (offset + 1) * length > total;
                int templength = islast ? (total - offset * length) : length;

                _hdfs.OpenFile(remotePath, offset * length, templength).ContinueWith(ds =>
                {
                    if (ds.Status == TaskStatus.RanToCompletion)
                    {
                        ds.Result.Content.CopyToAsync(fileStream).ContinueWith(cta =>
                        {
                            if (cta.Status == TaskStatus.RanToCompletion)
                            {
                                double value = 0;
                                if (islast)
                                {
                                    value = 1;
                                    log.ChangeStatusWithDispatcherInvoke(Enum.LogStatus.Success, OperationLogModel.Result_OK);
                                    fileStream.Flush();
                                    fileStream.Close();
                                }
                                else
                                {
                                    value = (double)((offset + 1) * length) / (double)total;
                                    log.ChangeStatusWithDispatcherInvoke(Enum.LogStatus.Loading, string.Concat((value * 100).ToString("0.00"), "%"));
                                }

                                if (value != 1)
                                {
                                    DownloadHDFSFile(remotePath, fileStream, offset + 1, length, total, log);
                                }
                            }
                            else throw new Exception(ds.Status.ToString());
                        });
                    }
                    else throw new Exception(ds.Status.ToString());
                });
            }
        }

        public Task AppendHDFSFile(string remotePath, Stream data, Action<Task<string>> continueTask)
        {
            if (_hdfs != null)
            {
                if (data != null && data.Length != 0)
                {
                    return _hdfs.AppendFile(data, remotePath).ContinueWith(continueTask);
                }
            }
            return null;
        }

        public Task GetHDFSFileStatus(string remotePath, Action<Task<DirectoryEntry>> continueTask)
        {
            if (_hdfs != null)
            {
                return _hdfs.GetFileStatus(remotePath).ContinueWith(continueTask);
            }
            else return null;
        }
    }
}